# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import time
import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.okex.kr_rest.native_public_client as npubc
from coin.exchange.base.kr_rest.rest_client_base import RestUpdate
from coin.exchange.okex.kr_rest.product import (
    get_active_symbol_list,
    OkexProduct,
)

from coin.proto.coin_market_query_pb2 import (
    ProductTicker,
    ExchangeTicker,
    ProductKlineElement,
    ProductKline,
)


class OkexFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg, product):
    ticker = update_msg['ticker']
    return ProductTicker(
        high=float(ticker['high']),
        low=float(ticker['low']),
        last=float(ticker['last']),
        ask=float(ticker['sell']),
        bid=float(ticker['buy']),
        volume_24hr=float(ticker['vol']),  # Last rolling 24 hours.
        exchange_timestamp=int(float(update_msg['date']) * 1e+9),
        native_symbol=product.native_symbol,
        symbol=product.symbol)

  @staticmethod
  def parse_native_kline(update_msg, product, kline_period):
    """Sample kline response:
    [
        [
            1417449600000,
            2339.11,
            2383.15,
            2322,
            2369.85,
            83850.06
        ],
        [
            1417536000000,
            2370.16,
            2380,
            2352,
            2367.37,
            17259.83
        ]
    ]
    """
    klines = update_msg
    kline_group = []
    for kline in klines:
      product_kline = ProductKlineElement(
          kline_timestamp=int(kline[0] * 1e+6),
          open=float(kline[1]),
          high=float(kline[2]),
          low=float(kline[3]),
          close=float(kline[4]),
          volume=float(kline[5]),
      )
      kline_group.append(product_kline)
    return ProductKline(
        symbol=product.symbol,
        native_symbol=product.native_symbol,
        exchange='Okex',
        kline_period=kline_period,
        klines=kline_group,
    )

  @staticmethod
  def parse_level_book(update_msg, product):
    """ BTC-USDT raw orderbook data:
    {
      "asks": [
        [ 10024.4656, 0.118 ],
        [ 10017.8961, 0.04710176 ],
        [ 10017.8958, 0.32458223 ]
      ],
      "bids": [
        [ 10017.8957, 5.52827749 ],
        [ 10010, 0.15050573 ],
        [ 10008.4396, 0.020331 ]
      ]
    }
    """
    level_book = level_book_mdl.LevelBook(product)

    for elem in update_msg['asks']:
      level_elem = level_book_mdl.LevelElement(float(elem[0]), float(elem[1]), 0)
      level_book.add_ask(level_elem)

    for elem in update_msg['bids']:
      level_elem = level_book_mdl.LevelElement(float(elem[0]), float(elem[1]), 0)
      level_book.add_bid(level_elem)
    level_book.finalize()
    return level_book


class OkexPublicClient(pubcb.PublicClientBase):
  ProductType = OkexProduct

  def __init__(self, timeout=1):
    self.npubc = npubc.OkexNativePublicClient(timeout=timeout)

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_ticker(symbol)
    update.msg = OkexFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    """Okex API does not allow query all tickers for all the products,
    so we need to query one by one. Only symbols in active_symbol_list are
    queried.
    """
    raw_tickers = []
    product_ticker_list = []
    req_time = time.time()
    for symbol in get_active_symbol_list():
      try:
        update = self.npubc.get_ticker(symbol)
        raw_tickers.append(update.msg)
        product = OkexProduct.FromStr(symbol)
        product_ticker_list.append(OkexFeedParser.parse_native_ticker(update.msg, product))
      except Exception as e:
        print('Fail to query ticker: %s-%s' % (product.exchange, product.symbol))
        continue
    res_time = time.time()

    exchange_ticker = ExchangeTicker(exchange='Okex', each_ticker=product_ticker_list)
    update = RestUpdate(req_time, res_time, raw_tickers)
    update.msg = exchange_ticker
    return update

  def query_history_kline_impl(self, product, kline_period, size):
    native_symbol = product.native_symbol
    update = self.npubc.get_history_kline(native_symbol, kline_period, size)
    update.msg = OkexFeedParser.parse_native_kline(update.msg, product, kline_period)
    return update

  def query_level_book_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_orderbook(symbol)
    update.msg = OkexFeedParser.parse_level_book(update.msg, product)
    return update
